// -*- C++ -*-

//========================================================================
/**
 * Author   : cuisw <shaovie@gmail.com>
 * Date     : 2009-05-29 02:24
 */
//========================================================================

#ifndef NDK_REACTOR_H_
#define NDK_REACTOR_H_

#include "ndk/types.h"
#include "ndk/guard.h"
#include "ndk/config.h"
#include "ndk/time_value.h"
#include "ndk/thread_mutex.h"
#include "ndk/reactor_impl.h"
#include "ndk/event_handler.h"

namespace ndk
{
  // Forward declarations
  //
  class reactor_impl;
  class event_handler;
  class token;

  /**
   * @class reactor
   *
   * @brief 
   */
  class reactor
  {
  public:
    /**
     * Create the reactor using @a implementation.  The flag
     * @a delete_implementation tells the reactor whether or not to
     * delete the @a implementation on destruction.
     */
    reactor(reactor_impl* implementation = 0, 
            int delete_implementation = 0);
    /**
     * Close down and release all resources.
     * Any notifications that remain queued on this reactor instance
     * are lost 
     */
    ~reactor();

    // Get pointer to a process-wide Reactor.
    static reactor *instance(void);

    /**
     * Set pointer to a process-wide reactor and return existing
     * pointer.  If <delete_reactor> != 0 then we'll delete the 
     * reactor at destruction time.
     */
    static reactor *instance(reactor *r);

    // Delete the dynamically allocated Singleton
    static void close_singleton(int delete_reactor = 0);

    // ++ reactor event loop management methods.
    // These methods work with an instance of a reactor.
    /**
     * Run the event loop until the 
     * <Reactor::handle_events/Reactor::alertable_handle_events>
     * method returns -1 or the <end_reactor_event_loop> method is invoked.
     */
    int run_reactor_event_loop();

    /**
     * Instruct the reactor to terminate its event loop and notifies the
     * reactor so that it can wake up and deactivate
     * itself. Deactivating the reactor would allow the reactor to be
     * shutdown gracefully. Internally the reactor calls deactivate()
     * on the underlying implementation.
     */ 
    void end_reactor_event_loop(void);

    // Indicate if the Reactor's event loop has been ended.
    int reactor_event_loop_done(void);

    /**
     * This event loop driver blocks for up to <max_wait_time> before
     * returning.  It will return earlier if events occur.  Note that
     * <max_wait_time> can be 0, in which case this method blocks
     * indefinitely until events occur.
     */
    int handle_events(const time_value *max_wait_time = 0);

    // ++ Register and remove handlers.
    /**
     * Register handler for I/O events.
     * The handle will come from event_handler::handle().
     *
     * If this handler/handle pair has already been registered, any 
     * new masks specified will be added.
     *
     * The reactor of the <eh> will be assigned to *this*
     * automatic, restore the original reactor if register failed
     */
    int register_handler(event_handler *eh,
                         reactor_mask mask);

    /**
     * Register handler for I/O events.
     * Same as register_handler(event_handler*, reactor_mask),
     * except handle is explicitly specified.
     */
    int register_handler(ndk_handle io_handle,
                         event_handler *eh,
                         reactor_mask mask);

    /**
     * Remove <masks> from <handle> registration.
     * For I/O handles, <masks> are removed from the Reactor.  Unless
     * <masks> includes <event_handler::DONT_CALL>, 
     * event_handler::handle_close() will be called with the <masks>
     * that have been removed. 
     */
    int remove_handler(ndk_handle handle, 
                       reactor_mask masks);

    /**
     * Remove <masks> from <eh> registration.
     * Same as remove_handler(ndk_handle, reactor_mask), except
     * <handle> comes from event_handler::handle().
     */
    int remove_handler(event_handler *eh,
                       reactor_mask masks);

    // ++ Timer management.

    /**
     * Schedule a timer event.
     * Schedule a timer event that will expire after an <delay> amount
     * of time.  The return value of this method, a timer_id value,
     * uniquely identifies the <eh> in the Reactor's internal
     * list of timers.  This timer_id value can be used to cancel the 
     * timer with the cancel_timer() call. 
     */
    int schedule_timer(event_handler *eh,
                       const void *arg,
                       const time_value &delay,
                       const time_value &interval = time_value::zero);

    // Format:
    // *       *       *       *       *
    // Min    Hour    Mday    Month   Wday

    // The fields are:
    // Min       minute of execution, 0-59
    // Hour      hour of execution, 0-23
    // Mday      day of month of execution, 1-31
    // Month     month of execution, 1-12 (or names)
    // Wday      day of week of execution, 0-7 (1 = monday, 0 or 7 = sunday, 
    //           or names)

    // Possible values:
    // *       matches all values, e.g. a * in month means: "every month"
    // x-y     matches the range x to y, e.g. 2-4 in Mday means "on the 
    //         2nd, 3rd, and 4th of the month"
    // x/n     in range x with frequency n, e.g. */2 in Hour means "every other hour"

    // Months can be names: jan, Feb, mAr (case insensitive). (use 3 chars)
    // Weekdays can be names, e.g. sun, Mon, tUe. (use 3 chars, no case)

    // Notes:
    // 1). Ranges and lists can be mixed. 
    //     e.g. 1,3-5 (means "1 3 4 5") 1,3-7/2 (means "1 3 5 7")
    // 2). Ranges can specify 'step' values. '10-16/2' is like '10,12,14,16'
    // 3). Ranges can be opened region. e.g. '2-' in hour means '2-23',
    //     '-15' in hour means '0-15', '-' is '*'
    // 4). List support '1,2,3,5-9,15-21/2,25/2,50-8,*/2,*,,' (this can be one entry,
    //     means '*', will calculate the collection of region.)

    // Special entries:
    // ------          -------
    // @yearly         Run once a year, "0 0 1 1 *".
    // @monthly        Run once a month, "0 0 1 * *".
    // @weekly         Run once a week, "0 0 * * 0".
    // @daily          Run once a day, "0 0 * * *".
    // @hourly         Run once an hour, "0 * * * *".

    // Examples:
    // '30 0 1 1,6,12 *'       00:30 Hrs on 1st of Jan, June & Dec.
    // '* * 1,15 * Sun'        Will run on the first and fifteenth AND every Sunday;
    // '* 20 * 10 1-5'         8.00 PM every weekday (Mon-Fri) only in Oct.
    // '0 7-23/3 * * *'        Every 3-hours while awake
    // '*/5 6-13 * * mon-fri'  Every 5 minutes during market hours
    // '0 12 * * 0,2,4'        Will run at noon every sun,tues,thurs 
    // '0 18-7 * * 1-5'        6pm-7am every hour during weekdays.
    int crontab(const event_handler *eh, const void *arg, const char *entry);

    /**
     * Reset recurring timer interval.
     *
     * Resets the interval of the timer represented by  timer_id to
     * interval, which is specified in relative time to the current
     * <gettimeofday>.  If <interval> is equal to time_value::zero,
     * the timer will become a non-rescheduling timer.  Returns 0 
     * if successful, -1 if not.
     * This change will not take effect until the next timeout.
     */
    int reset_timer_interval(int timer_id,
                             const time_value &interval);

    /**
     * Cancel timer.
     *
     * Cancel timer associated with timer_id that was returned from
     * the schedule_timer() method.  If arg is non-NULL then it will be
     * set to point to the argument passed in when the handler was 
     * registered. This makes it possible to free up the memory and 
     * avoid memory leaks.  Returns 0 if cancellation succeeded and -1 
     * if the  timer_id wasn't found.
     *
     * On successful cancellation, event_handler::handle_close() will 
     * be called when <dont_call_handle_close> is zero, handle_close() 
     * will pass the argument passed in when the handler was 
     * registered, This makes it possible to release the memory of 
     * the <arg> in handle_close().
     * avoid memory leaks, also pass the argument <event_handler::TIMER_MASK>. 
     */
    int cancel_timer(int timer_id,
                     const void **arg = 0,
                     int dont_call_handle_close = 1);

    /**
     * Cancel all timers associated with event handler.
     *
     * Shorthand for calling cancel_timer(long,const void **,int) 
     * multiple times for all timer associated with <eh>.
     *
     * event_handler::handle_close() will be called when 
     * <dont_call_handle_close> is zero, but not pass the argument 
     * passed in when the handler was registered, because it could 
     * register multiple argument<arg>, I don't know which should be
     * passed.
     *
     * Returns number of handlers cancelled.
     */
    int cancel_timer(event_handler *eh,
                     int dont_call_handle_close = 1);
    /**
     * Dispatch user specified events.
     *
     * Handler will be dispatched irrespective of whether it is
     * registered, not registered, or suspended in the Reactor.
     */
    int notify(event_handler *eh = 0,
               reactor_mask mask = event_handler::except_mask);
    /**
     * Purge any notifications pending in this reactor for the specified
     * event_handler object. If eh == 0, all notifications for
     * all handlers are removed (but not any notifications posted just
     * to wake up the reactor itself). Returns the number of
     * notifications purged.  Returns -1 on error.
     */
    int purge_pending_notifications(event_handler *eh = 0,
                                    reactor_mask mask = event_handler::all_events_mask);

    // Get token.
    token &lock();

    // Get the implementation class
    reactor_impl* implementation();

    // Returns the current size of the Reactor's internal descriptor
    // table
    size_t size(void);

    // Delegation/implementation class that all methods will be
    // forwarded to.
    reactor_impl *reactor_impl_;

    // Flag used to indicate whether we are responsible for cleaning up
    // the implementation instance
    int delete_impl_;

    // Pointer to a process-wide reactor singleton.
    static reactor *reactor_;
    static thread_mutex instance_lock_;

  private:
    // Deny access since member-wise won't work...
    reactor(const reactor &);
    reactor &operator =(const reactor &);
  };
} // namespace ndk

#include "ndk/reactor.inl"
#endif // NDK_REACTOR_H_

